use std::path;

use ggez::{
    conf::{WindowMode, WindowSetup},
    event,
    input::keyboard::{KeyCode, KeyInput},
    Context, ContextBuilder, GameResult,
};
use resources::{InputQueue, Time};
use specs::{RunNow, World, WorldExt};

mod audio;
mod components;
mod constants;
mod entities;
mod events;
mod map;
mod resources;
mod systems;

// componts

// This struct will hold all our game state
// For now there is nothing to be held, but we'll add
// things shortly.
struct Game {
    world: World,
}

// This is the main event loop. ggez tells us to implement
// two things:
// - updating
// - rendering
impl event::EventHandler<ggez::GameError> for Game {
    fn update(&mut self, context: &mut ggez::Context) -> Result<(), ggez::GameError> {
        // Run input system
        {
            let mut is = systems::InputSystem {};
            is.run_now(&self.world);
        }
        // Run gameplay state system
        {
            let mut gss = systems::GameplayStateSystem {};
            gss.run_now(&self.world);
        }
        // Get and update time resource
        {
            let mut time = self.world.write_resource::<Time>();
            time.delta += context.time.delta()
        }
        // Run event system
        {
            let mut es = systems::EventSystem { context };
            es.run_now(&self.world);
        }
        Ok(())
    }

    fn draw(&mut self, context: &mut ggez::Context) -> Result<(), ggez::GameError> {
        // Render game entities
        {
            let mut rs = systems::RenderingSystem { context };
            rs.run_now(&self.world);
        }
        Ok(())
    }

    fn key_down_event(
        &mut self,
        _ctx: &mut Context,
        input: KeyInput,
        _repeated: bool,
    ) -> Result<(), ggez::GameError> {
        if let Some(keycode) = input.keycode {
            match keycode {
                KeyCode::Up | KeyCode::Down | KeyCode::Left | KeyCode::Right => {
                    let mut input_queue = self.world.write_resource::<InputQueue>();
                    input_queue.keys_pressed.push(keycode);
                }
                _ => (),
            }
        }
        Ok(())
    }
}

// Initialize the level
pub fn initialize_level(world: &mut World) {
    const MAP: &str = "
    N N W W W W W W
    W W W . . . . W
    W . . . BB . . W
    W . . RB . . . W
    W . P . . . . W
    W . . . . RS . W
    W . . BS . . . W
    W . . . . . . W
    W W W W W W W W
    ";
    map::load_map(world, MAP.to_string());
}

fn main() -> GameResult {
    let mut world = World::new();

    components::register_components(&mut world);
    resources::register_resources(&mut world);

    initialize_level(&mut world);

    // Create a game context and event loop
    let context_builder = ContextBuilder::new("rust_sokoban", "sokoban")
        .window_setup(WindowSetup::default().title("Rust Sokoban!"))
        .window_mode(WindowMode::default().dimensions(800.0, 600.0))
        .add_resource_path(path::Path::new("./resources"));

    let (mut ctx, event_loop) = context_builder.build()?;

    // Initialize audio
    audio::initialize_sounds(&mut world, &mut ctx);

    // Create the game state
    let game = Game { world };
    // Run the main event loop
    event::run(ctx, event_loop, game)
}
